package jtg.generator;


import jtg.util.CommonUtil;
import jtg.util.PathUtil;
import soot.Local;
import soot.Unit;
import soot.ValueBox;
import soot.toolkits.graph.Block;
import soot.toolkits.graph.BlockGraph;
import soot.toolkits.graph.BriefBlockGraph;

import java.util.*;

/**
 * 思路：
 * 从未选择的跳转开始搞
 * 向前DFS到开始节点结束
 * 向后DFS到结束节点，得到完整路径，求解约束
 * 如果有解，完整路径+1，测试数据+1
 * 如果无解，继续DFS，找到的Path数量+1
 * 重复以上两步骤，直到Path数量==N，放弃，并告诉此路径不可解
 */
public class DataFlowBasedGenerator extends GeneralGenerator{
    //key为变量，value为def这个变量的那些Unit的Set
    private HashMap<String, Set<Unit>> defMap = new HashMap<>();

    //key为变量，value为use这个变量的那些Unit的Set
    private HashMap<String, Set<Unit>> useMap = new HashMap<>();

    //所有变量的 du(i,j,v)
    //外部的map，key为变量名
    //内部的map，key为 "i,j" 的字符串，value为 du(i,j,v)
    HashMap<String, HashMap<String, Set<List<Unit>>>> duPaths = new HashMap<>();


    public DataFlowBasedGenerator(String classPath, String className, String methodName) {
        super(classPath, className, methodName);
    }

    @Override
    void init() {
        initSet = new HashSet<>(getPathsForADUPC()); //初始集合是所有的基路径
        solvableSet = new HashSet<>();
        unsolvableSet = new HashSet<>(initSet);
        testData = new HashSet<>();
    }

    @Override
    Set<List<Unit>> calAllFullCandidate(Object o) {
        List<Unit> branch = (List)o;
        Unit headOfBranch = branch.get(0);
        Unit tailOfBranch = branch.get(branch.size()-1);
        Set<List<Unit>> backwardPaths = new HashSet<>();
        Set<List<Unit>> forwardPaths = new HashSet<>();
        for (Unit head : heads) {
            PathUtil pathUtil = new PathUtil();
            pathUtil.findPath(ug, headOfBranch, head, new ArrayList<>(), true, new HashMap<>());
            backwardPaths.addAll(pathUtil.getSearchPathResult());
        }
        for (Unit tail : tails) {
            PathUtil pathUtil = new PathUtil();
            pathUtil.findPath(ug, tailOfBranch, tail, new ArrayList<>(), false, new HashMap<>());
            forwardPaths.addAll(pathUtil.getSearchPathResult());
        }
        Set<List<Unit>> result = new HashSet<>();
        if (backwardPaths.isEmpty() || forwardPaths.isEmpty()) {
            return result; //没有完整路径，返回空
        }
        for (List<Unit> backwardPath : backwardPaths) {
            Collections.reverse(backwardPath); //这个地方注意，每次得复制一下
            for (List<Unit> forwardPath : forwardPaths) {
                List<Unit> backwardPathCopy = new ArrayList<>(backwardPath);
                List<Unit> forwardPathCopy = new ArrayList<>(forwardPath); //注意不能直接操作
                backwardPathCopy.remove(backwardPathCopy.size()-1); //首尾重合了
                backwardPathCopy.addAll(branch);
                forwardPathCopy.remove(0);
                backwardPathCopy.addAll(forwardPathCopy);//拼接到一起
                result.add(backwardPathCopy);
            }
        }
        return result;
    }

    @Override
    void checkCov(List<Unit> fullPath) {
        for (Object o : unsolvableSet) {
            List<Unit> branch = (List<Unit>) o;
            if (CommonUtil.leftIsSubList(branch, fullPath)) {
                solvableSet.add(branch);
            }
        }
        unsolvableSet.removeAll(solvableSet);
    }



    private void calculate() {

        //defMap useMap
        Iterator<Unit> iterator = ug.iterator();
        while (iterator.hasNext()) {
            System.out.println("--------------------------------");
            Unit unit = iterator.next();
            //这个节点def了哪些变量
            List<ValueBox> defBoxes = unit.getDefBoxes();
            for (ValueBox valueBox : defBoxes) {
                String varName = valueBox.getValue().toString();
                if (!defMap.containsKey(varName)) {
                    HashSet<Unit> temp = new HashSet<>();
                    defMap.put(varName, temp);
                }
                Set<Unit> set = defMap.get(varName);
                set.add(unit);
            }
            //这个节点use了哪些变量
            List<ValueBox> useBoxes = unit.getUseBoxes();
            for (ValueBox valueBox : useBoxes) {
                String varName = valueBox.getValue().toString();
                if (!useMap.containsKey(varName)) {
                    HashSet<Unit> temp = new HashSet<>();
                    useMap.put(varName, temp);
                }
                Set<Unit> set = useMap.get(varName);
                set.add(unit);
            }
        }
        System.out.println(defMap);
        System.out.println(useMap);
        PathUtil pathUtil = new PathUtil();
        //duPaths
        Iterator<Local> iterator2 = body.getLocals().iterator();
        while (iterator2.hasNext()) {
            String var = iterator2.next().getName();
            //def这个var的那些units
            Set<Unit> defUnits = defMap.get(var);
            //use这个var的那些units
            Set<Unit> useUnits = useMap.get(var);
            if (defUnits == null || useUnits == null) {
                continue;
            }
            for (Unit defUnit : defUnits) {
                for (Unit useUnit : useUnits) {
                    String key = defUnit.toString() + "," + useUnit.toString();
                    //尝试寻找，有没有从defUnit到useUnit的关于var的du-path
                    ArrayList<Unit> path = new ArrayList<>();
                    path.add(defUnit);
                    //du(i,j,v)
                   pathUtil.findPath(ug,defUnit, useUnit, new ArrayList<>(), false, new HashMap<>());
                    Set<List<Unit>> duPathSet =new HashSet<>();
                            duPathSet.addAll(pathUtil.getSearchPathResult());

                    if (!duPaths.containsKey(var)) {
                        HashMap<String, Set<List<Unit>>> temp = new HashMap<>();
                        duPaths.put(var, temp);
                    }
                    HashMap<String, Set<List<Unit>>> temp = duPaths.get(var);
                    temp.put(key, duPathSet);

                }
            }
        }
        System.out.println(duPaths);

    }
    private ArrayList<List<Unit>> getPathsForADUPC() {
        calculate();
        //把所有路径都拿出来就完事了
        ArrayList<List<Unit>> paths = new ArrayList<>();
        for (String varName : duPaths.keySet()) {
            HashMap<String, Set<List<Unit>>> map = duPaths.get(varName);
            for (String fromTo : map.keySet()) {
                Set<List<Unit>> pathSet = map.get(fromTo);
                paths.addAll(pathSet);
            }
        }
        return paths;
    }
}
